/************************************************************************
 *   IRC - Internet Relay Chat, common/usend.c
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *		      University of Oulu, Computing Center
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 1, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* -- Jto -- 16 Jun 1990
 * Added Armin's PRIVMSG patches...
 */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include <stdio.h>

#define NEWLINE	"\r\n"

static char sendbuf[2048];
static int send_message PROTO ((aClient *, char *, int));

static int sentalong[MAXCONNECTIONS];

/*
   ** dead_link
   **   An error has been detected. The link *must* be closed,
   **   but *cannot* call ExitClient (m_bye) from here.
   **   Instead, mark it with FLAGS_DEADSOCKET. This should
   **   generate ExitClient from the main loop.
   **
   **   If 'notice' is not NULL, it is assumed to be a format
   **   for a message to local opers. I can contain only one
   **   '%s', which will be replaced by the sockhost field of
   **   the failing link.
   **
   **   Also, the notice is skipped for "uninteresting" cases,
   **   like Persons and yet unknown connections...
 */
static int dead_link (to, notice)
     aClient *to;
     char *notice;
{
    if (to->flags2 & FLAGS2_HTC) {
	if (MyClient (to))	  /* Joins large channels increase the htccount */
	    to->htccount += 5;
	return 0;
    }

    to->flags |= FLAGS_DEADSOCKET;
    /*
     * If because of BUFFERPOOL problem then clean dbuf's now so that
     * notices don't hurt operators below.
     */
    DBufClear (&to->recvQ);
    DBufClear (&to->sendQ);
    if (!IsPerson (to) && !IsUnknown (to) && !(to->flags & FLAGS_CLOSING))
	(void) sendto_failops_whoare_opers (notice,
					    get_client_name (to, FALSE));
    Debug ((DEBUG_ERROR, notice, get_client_name (to, FALSE)));
    return -1;
}

/*
   ** flush_connections
   **   Used to empty all output buffers for all connections. Should only
   **   be called once per scan of connections. There should be a select in
   **   here perhaps but that means either forcing a timeout or doing a poll.
   **   When flushing, all we do is empty the obuffer array for each local
   **   client and try to send it. if we cant send it, it goes into the sendQ
   **   -avalon
 */
void flush_connections (fd)
     int fd;
{
    int i;
    aClient *cptr;

    if (fd == me.fd) {
	for (i = highest_fd; i >= 0; i--)
	    if ((cptr = local[i]) && DBufLength (&cptr->sendQ) > 0)
		(void) send_queued (cptr);

    }
    else if (fd >= 0 && (cptr = local[fd]) && DBufLength (&cptr->sendQ) > 0) {
	(void) send_queued (cptr);
    }
}

/*
   ** send_message
   **   Internal utility which delivers one message buffer to the
   **   socket. Takes care of the error handling and buffering, if
   **   needed.
 */
static int send_message (aClient *to, char *msg, int len)
{
	/* if msg is a null pointer, we are flushing connection */
    static int SQinK;

    if (IsDead (to))
	return 0;		  /* This socket has already been marked as dead */
    if (DBufLength (&to->sendQ) > get_sendq (to)) {
	if (IsServer (to))
	    sendto_ops ("Max SendQ limit exceeded for %s: %d > %d",
			get_client_name (to, FALSE),
			DBufLength (&to->sendQ), get_sendq (to));
	return dead_link (to, "Max Sendq exceeded");
    }
    else {

#ifdef ZIP_LINKS
	/*
	 * data is first stored in to->zip->outbuf until
	 * it's big enough to be compressed and stored in the sendq.
	 * send_queued is then responsible to never let the sendQ
	 * be empty and to->zip->outbuf not empty.
	 */

	me.u_sendB += len;

	if (me.u_sendB > 1023) {
	    me.u_sendK += (me.u_sendB >> 10);
	    me.u_sendB &= 0x03ff;
	}

	if (to->flags2 & FLAGS2_ZIP) {
	    msg = zip_buffer (to, msg, &len, 0);
	}
    }
    if (len && dbuf_put (&to->sendQ, msg, len) < 0)
#else
    }
    if (dbuf_put (&to->sendQ, msg, len) < 0)
#endif
	return dead_link (to, "Buffer allocation error for %s");

    /*
       ** Update statistics. The following is slightly incorrect
       ** because it counts messages even if queued, but bytes
       ** only really sent. Queued bytes get updated in SendQueued.
     */
    to->sendM += 1;
    me.sendM += 1;
    if (to->acpt != &me)
	to->acpt->sendM += 1;

    /*
       ** This little bit is to stop the sendQ from growing too large when
       ** there is no need for it to. Thus we call send_queued() every time
       ** 2k has been added to the queue since the last non-fatal write.
       ** Also stops us from deliberately building a large sendQ and then
       ** trying to flood that link with data (possible during the net
       ** relinking done by servers with a large load).
     */
    SQinK = DBufLength (&to->sendQ) / 1024;

    if (IsServer (to)) {
	if (SQinK > to->lastsq) {
	    send_queued (to);
	}
    }
    else {
	if (SQinK > (to->lastsq + 4))
	    send_queued (to);
    }

    return 0;
}

/*
** send_queued
**   This function is called from the main select-loop (or whatever)
**   when there is a chance the some output would be possible. This
**   attempts to empty the send queue as far as possible...
*/
int send_queued (to)
     aClient *to;
{
    char *msg;
    int len, rlen, more = 0;

#ifndef pyr
    if (IsBlocked (to))
	return -1;		  /* Can't write to already blocked socket */
#endif /* pyr */

    /*
       ** Once socket is marked dead, we cannot start writing to it,
       ** even if the error is removed...
     */
    if (IsDead (to)) {
	/*
	   ** Actually, we should *NEVER* get here--something is
	   ** not working correct if send_queued is called for a
	   ** dead socket... --msa
	 */
	return -1;
    }
#ifdef ZIP_LINKS
    if ((to->flags2 & FLAGS2_ZIP) && to->zip->outcount) {
	if (DBufLength (&to->sendQ) > 0)
	    more = 1;
	else {
	    msg = zip_buffer (to, NULL, &len, 1);

	    if (len == -1)
		return dead_link (to, "Fatal error in zip_buffer()");

	    if (dbuf_put (&to->sendQ, msg, len) < 0)
		return dead_link (to, "Buffer allocation error for %s",
				  get_client_name (to, TRUE));
	}
    }
#endif

    while (DBufLength (&to->sendQ) > 0 || more) {
	msg = dbuf_map (&to->sendQ, &len);
	/* Returns always len > 0 */
	if ((rlen = deliver_it (to, msg, len)) < 0)
	    return dead_link (to, "Write error to %s, closing link");
	(void) dbuf_delete (&to->sendQ, rlen);
	to->lastsq = DBufLength (&to->sendQ) / 1024;
	if (rlen < len) {
	    /* If we can't write full message, mark the socket
	     * as "blocking" and stop trying. -Donwulff */
	    SetBlocked (to);
	    break;
	}

#ifdef ZIP_LINKS
	if (DBufLength (&to->sendQ) == 0 && more) {
	    more = 0;

	    msg = zip_buffer (to, NULL, &len, 1);

	    if (len == -1)
		return dead_link (to, "Fatal error in zip_buffer()");

	    if (dbuf_put (&to->sendQ, msg, len) < 0)
		return dead_link (to, "Buffer allocation error for %s",
				  get_client_name (to, TRUE));
	}
#endif

    }

    return (IsDead (to)) ? -1 : 0;
}

/*
   ** send message to single client
 */
void sendto_one (to, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
     aClient *to;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
{

    (void) sprintf (sendbuf, pattern, p1, p2, p3, p4, p5, p6,
		    p7, p8, p9, p10, p11);
    Debug ((DEBUG_SEND, "Sending [%s] to %s", sendbuf, to->name));

    if (to->from)
	to = to->from;
    if (to->fd < 0) {
	Debug ((DEBUG_ERROR,
		"Local socket %s with negative fd... AARGH!", to->name));
    }
    else if (IsMe (to)) {
	sendto_ops ("Trying to send [%s] to myself!", sendbuf);
	return;
    }
    (void) strcat (sendbuf, NEWLINE);
    sendbuf[510] = '\r';
    sendbuf[511] = '\n';
    sendbuf[512] = '\0';
    (void) send_message (to, sendbuf, strlen (sendbuf));
}

sendto_channel_butone (one, from, chptr, pattern,
		       p1, p2, p3, p4, p5, p6, p7, p8)
     aClient *one, *from;
     aChannel *chptr;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    Link *lp;
    aClient *acptr;
    int i;

    bzero (sentalong, sizeof (sentalong));
    for (lp = chptr->members; lp; lp = lp->next) {
	acptr = lp->value.cptr;
	if (acptr->from == one || (lp->flags & CHFL_ZOMBIE))
	    continue;		  /* ...was the one I should skip */
	i = acptr->from->fd;
	if (MyConnect (acptr) && IsRegisteredUser (acptr)) {
	    sendto_prefix_one (acptr, from, pattern, p1, p2,
			       p3, p4, p5, p6, p7, p8);
	    sentalong[i] = 1;
	}
	else {
	    /* Now check whether a message has been sent to this
	     * remote link already */
	    if (sentalong[i] == 0) {
		sendto_prefix_one (acptr, from, pattern,
				   p1, p2, p3, p4, p5, p6, p7, p8);
		sentalong[i] = 1;
	    }
	}
    }
    return;
}

/*
 * sendto_channelops_butone Added 1 Sep 1996 by Cabal95.
 *   Send a message to all OPs in channel chptr that
 *   are directly on this server and sends the message
 *   on to the next server if it has any OPs.
 *
 *   All servers must have this functional ability
 *    or one without will send back an error message. -- Cabal95
 */
void
sendto_channelops_butone (aClient *one, aClient *from, aChannel *chptr, char *pattern,
			  char *p1, char *p2, char *p3, char *p4,
			  char *p5, char *p6, char *p7, char *p8)
{
    Link *lp;
    aClient *acptr;
    int i;

    for (i = 0; i < MAXCONNECTIONS; i++)
	sentalong[i] = 0;
    for (lp = chptr->members; lp; lp = lp->next) {
	acptr = lp->value.cptr;
	if (acptr->from == one || (lp->flags & CHFL_ZOMBIE) ||
	    !(lp->flags & CHFL_CHANOP) || !(lp->flags & CHFL_HALFOP))
	    continue;		  /* ...was the one I should skip
				     or user not not a channel op */
				     /* or not not a channel halfop -- Kei */
	i = acptr->from->fd;
	if (MyConnect (acptr) && IsRegisteredUser (acptr)) {
	    sendto_prefix_one (acptr, from, pattern, p1, p2,
			       p3, p4, p5, p6, p7, p8);
	    sentalong[i] = 1;
	}
	else {
	    /* Now check whether a message has been sent to this
	     * remote link already */
	    if (sentalong[i] == 0) {
		sendto_prefix_one (acptr, from, pattern,
				   p1, p2, p3, p4, p5, p6, p7, p8);
		sentalong[i] = 1;
	    }
	}
    }
    return;
}

/*
 * sendto_channelvoice_butone
 * direct port of Cabal95's sendto_channelops_butone
 * to allow for /notice @+#channel messages
 * not exactly the most adventurous coding (made heavy use of copy-paste) <G>
 * but it's needed to avoid mass-msg trigger in script vnotices
 * -DuffJ
 */

void
sendto_channelvoice_butone (one, from, chptr, pattern,
			    p1, p2, p3, p4, p5, p6, p7, p8)
     aClient *one, *from;
     aChannel *chptr;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    Link *lp;
    aClient *acptr;
    int i;

    for (i = 0; i < MAXCONNECTIONS; i++)
	sentalong[i] = 0;
    for (lp = chptr->members; lp; lp = lp->next) {
	acptr = lp->value.cptr;
	if (acptr->from == one || (lp->flags & CHFL_ZOMBIE) ||
	    !((lp->flags & CHFL_VOICE) || (lp->flags & CHFL_HALFOP) || (lp->flags & CHFL_CHANOP)))
	    continue;		  /* ...was the one I should skip
				     or user not (a channel voice or op) */
	i = acptr->from->fd;
	if (MyConnect (acptr) && IsRegisteredUser (acptr)) {
	    sendto_prefix_one (acptr, from, pattern, p1, p2,
			       p3, p4, p5, p6, p7, p8);
	    sentalong[i] = 1;
	}
	else {
	    /* Now check whether a message has been sent to this
	     * remote link already */
	    if (sentalong[i] == 0) {
		sendto_prefix_one (acptr, from, pattern,
				   p1, p2, p3, p4, p5, p6, p7, p8);
		sentalong[i] = 1;
	    }
	}
    }
    return;
}

/*
 * sendto_server_butone
 *
 * Send a message to all connected servers except the client 'one'.
 */
void sendto_serv_butone (one, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     aClient *one;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    int i;
    aClient *cptr;

#ifdef NPATH
    check_command ((long) 2, pattern, p1, p2, p3);
#endif

    for (i = 0; i <= highest_fd; i++) {
	if (!(cptr = local[i]) || (one && cptr == one->from))
	    continue;
	if (IsServer (cptr))
	    sendto_one (cptr, pattern, p1, p2, p3, p4, p5, p6, p7, p8);
    }
    return;
}

/*
 * sendto_SNICK_butone (send to servers that support SNICK)
 *
 * Send a message to all connected servers except the client 'one'.
 */
void sendto_SNICK_butone (one, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9)
     aClient *one;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9;
{
    int i;
    aClient *cptr;

#ifdef NPATH
    check_command ((long) 2, pattern, p1, p2, p3);
#endif

    for (i = 0; i <= highest_fd; i++) {
	if (!(cptr = local[i]) || (one && cptr == one->from))
	    continue;
	if (IsServer (cptr))
	    sendto_one (cptr, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9);
    }
    return;
}

/*
 * sendto_serv_butone_quit
 *
 * Send a message to all connected servers except the client 'one'.
 * BUT, don't send to NOQUIT servers.
 */
void sendto_serv_butone_quit (one, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     aClient *one;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    int i;
    aClient *cptr;

#ifdef NPATH
    check_command ((long) 2, pattern, p1, p2, p3);
#endif

    for (i = 0; i <= highest_fd; i++) {
	if (!(cptr = local[i]) || (one && cptr == one->from))
	    continue;
	if (IsServer (cptr) && !DontSendQuit (cptr))
	    sendto_one (cptr, pattern, p1, p2, p3, p4, p5, p6, p7, p8);
    }
    return;
}

/*
 * sendto_common_channels()
 *
 * Sends a message to all people (inclusing user) on local server who are
 * in same channel with user.
 */
void sendto_common_channels (user, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     aClient *user;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    aClient *cptr;
    Link *lp, *lp2;

    bzero (sentalong, sizeof (sentalong));
    sentalong[0] = 1;
    for (lp = user->user->channel; lp; lp = lp->next)
	for (lp2 = lp->value.chptr->members; lp2; lp2 = lp2->next) {
	    cptr = lp2->value.cptr;
	    if (!MyConnect (cptr))
		continue;
	    if (sentalong[cptr->fd])
		continue;
	    sentalong[cptr->fd] = 1;
	    sendto_prefix_one (cptr, user, pattern, p1, p2, p3, p4, p5, p6,
			       p7, p8);
	}
    if (MyConnect (user) && !sentalong[user->fd])
	sendto_prefix_one (user, user, pattern, p1, p2, p3, p4, p5, p6, p7,
			   p8);
    return;
}

/*
 * sendto_channel_butserv
 *
 * Send a message to all members of a channel that are connected to this
 * server.
 */
void
sendto_channel_butserv (chptr, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     aChannel *chptr;
     aClient *from;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    Link *lp;
    aClient *acptr;

    for (lp = chptr->members; lp; lp = lp->next)
	if (MyConnect (acptr = lp->value.cptr) && !(lp->flags & CHFL_ZOMBIE))
	    sendto_prefix_one (acptr, from, pattern,
			       p1, p2, p3, p4, p5, p6, p7, p8);

    return;
}

/*
   ** send a msg to all ppl on servers/hosts that match a specified mask
   ** (used for enhanced PRIVMSGs)
   **
   ** addition -- Armin, 8jun90 (gruner@informatik.tu-muenchen.de)
 */

static int match_it (one, mask, what)
     aClient *one;
     char *mask;
     int what;
{
    switch (what) {
    case MATCH_HOST:
	return (match (mask, one->user->host) == 0);
    case MATCH_SERVER:
    default:
	return (match (mask, one->user->server) == 0);
    }
}

/*
 * sendto_match_servs
 *
 * send to all servers which match the mask at the end of a channel name
 * (if there is a mask present) or to all if no mask.
 */
void
sendto_match_servs (chptr, from, format, p1, p2, p3, p4, p5, p6, p7, p8, p9)
     aChannel *chptr;
     aClient *from;
     char *format, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9;
{
    int i;
    aClient *cptr;
    char *mask;

#ifdef NPATH
    check_command ((long) 3, format, p1, p2, p3);
#endif
    if (chptr) {
	if (*chptr->chname == '&')
	    return;
	if ((mask = (char *) rindex (chptr->chname, ':')))
	    mask++;
    }
    else
	mask = (char *) NULL;

    for (i = 0; i <= highest_fd; i++) {
	if (!(cptr = local[i]))
	    continue;
	if ((cptr == from) || !IsServer (cptr))
	    continue;
	if (!BadPtr (mask) && IsServer (cptr) && match (mask, cptr->name))
	    continue;
	sendto_one (cptr, format, p1, p2, p3, p4, p5, p6, p7, p8, p9);
    }
}

/*
 * sendto_match_butone
 *
 * Send to all clients which match the mask in a way defined on 'what';
 * either by user hostname or user servername.
 */
void
sendto_match_butone (one, from, mask, what, pattern,
		     p1, p2, p3, p4, p5, p6, p7, p8)
     aClient *one, *from;
     int what;
     char *mask, *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    int i;
    aClient *cptr, *acptr;
    char cansendlocal, cansendglobal;

    if (MyConnect (from)) {
	cansendlocal = (OPCanLNotice (from)) ? 1 : 0;
	cansendglobal = (OPCanGNotice (from)) ? 1 : 0;
    }
    else
	cansendlocal = cansendglobal = 1;

    for (i = 0; i <= highest_fd; i++) {
	if (!(cptr = local[i]))
	    continue;		  /* that clients are not mine */
	if (cptr == one)	  /* must skip the origin !! */
	    continue;
	if (IsServer (cptr)) {
	    if (!cansendglobal)
		continue;
	    for (acptr = client; acptr; acptr = acptr->next)
		if (IsRegisteredUser (acptr)
		    && match_it (acptr, mask, what) && acptr->from == cptr)
		    break;
	    /* a person on that server matches the mask, so we
	       ** send *one* msg to that server ...
	     */
	    if (acptr == NULL)
		continue;
	    /* ... but only if there *IS* a matching person */
	}
	/* my client, does he match ? */
	else if (!cansendlocal || (!(IsRegisteredUser (cptr) &&
				     match_it (cptr, mask, what))))
	    continue;
	sendto_prefix_one (cptr, from, pattern, p1, p2, p3, p4, p5, p6, p7,
			   p8);
    }
    return;
}

/*
 * sendto_all_butone.
 *
 * Send a message to all connections except 'one'. The basic wall type
 * message generator.
 */
void sendto_all_butone (one, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     aClient *one, *from;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    int i;
    aClient *cptr;

    for (i = 0; i <= highest_fd; i++)
	if ((cptr = local[i]) && !IsMe (cptr) && one != cptr)
	    sendto_prefix_one (cptr, from, pattern, p1, p2, p3, p4, p5, p6,
			       p7, p8);

    return;
}

/*
 * sendto_ops
 *
 *  Send to *local* ops only.
 */
void sendto_ops (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    aClient *cptr;
    int i;
    char nbuf[1024];

    for (i = 0; i <= highest_fd; i++)
	if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
	    IsAnOper (cptr) && SendServNotice (cptr)) {
	    (void) sprintf (nbuf, ":%s NOTICE %s :*** Notice -- ",
			    me.name, cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
#ifdef	USE_SERVICES
	else if (cptr && IsService (cptr) &&
		 (cptr->service->wanted & SERVICE_WANT_SERVNOTE)) {
	    (void) sprintf (nbuf, "NOTICE %s :*** Notice -- ", cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
#endif /* USE_SERVICES */
    return;
}

/*
 * sendto_failops
 *
 *      Send to *local* mode +g ops only.
 */
void sendto_failops (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    aClient *cptr;
    int i;
    char nbuf[1024];

    for (i = 0; i <= highest_fd; i++)
	if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
	    SendFailops (cptr)) {
	    (void) sprintf (nbuf, ":%s NOTICE %s :*** Global -- ",
			    me.name, cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
#ifdef  USE_SERVICES
	else if (cptr && IsService (cptr) &&
		 (cptr->service->wanted & SERVICE_WANT_SERVNOTE)) {
	    (void) sprintf (nbuf, "NOTICE %s :*** Notice -- ", cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
#endif
    return;
}

/*
 * sendto_umode
 *
 *  Send to specified umode
 */
void sendto_umode (umodes, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     int umodes;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    aClient *cptr;
    int i;
    char nbuf[1024];

    for (i = 0; i <= highest_fd; i++)
	if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
	    (cptr->umodes & umodes) == umodes) {
	    (void) sprintf (nbuf, ":%s NOTICE %s :", me.name, cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
    return;
}

/*
 * sendto_failops_whoare_opers
 *
 *      Send to *local* mode +g ops only who are also +o.
 */
void sendto_failops_whoare_opers (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    aClient *cptr;
    int i;
    char nbuf[1024];

    for (i = 0; i <= highest_fd; i++)
	if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
	    SendFailops (cptr) && IsAnOper (cptr)) {
	    (void) sprintf (nbuf, ":%s NOTICE %s :*** Global -- ",
			    me.name, cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
#ifdef  USE_SERVICES
	else if (cptr && IsService (cptr) &&
		 (cptr->service->wanted & SERVICE_WANT_SERVNOTE)) {
	    (void) sprintf (nbuf, "NOTICE %s :*** Notice -- ", cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
#endif
    return;
}

/*
 * sendto_locfailops
 *
 *      Send to *local* mode +g ops only who are also +o.
 */
void sendto_locfailops (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    aClient *cptr;
    int i;
    char nbuf[1024];

    for (i = 0; i <= highest_fd; i++)
	if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
	    SendFailops (cptr) && IsAnOper (cptr)) {
	    (void) sprintf (nbuf, ":%s NOTICE %s :*** LocOps -- ",
			    me.name, cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
#ifdef  USE_SERVICES
	else if (cptr && IsService (cptr) &&
		 (cptr->service->wanted & SERVICE_WANT_SERVNOTE)) {
	    (void) sprintf (nbuf, "NOTICE %s :*** Notice -- ", cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
#endif
    return;
}

/*
 * sendto_opers
 *
 *  Send to *local* ops only. (all +O or +o people)
 */
void sendto_opers (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    aClient *cptr;
    int i;
    char nbuf[1024];

    for (i = 0; i <= highest_fd; i++)
	if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
	    IsAnOper (cptr)) {
	    (void) sprintf (nbuf, ":%s NOTICE %s :*** Oper -- ",
			    me.name, cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
#ifdef	USE_SERVICES
	else if (cptr && IsService (cptr) &&
		 (cptr->service->wanted & SERVICE_WANT_SERVNOTE)) {
	    (void) sprintf (nbuf, "NOTICE %s :*** GLOBAL OPER Notice -- ",
			    cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
#endif
    return;
}

/* ** sendto_ops_butone
   **   Send message to all operators.
   ** one - client not to send message to
   ** from- client which message is from *NEVER* NULL!!
 */
void sendto_ops_butone (one, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     aClient *one, *from;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    int i;
    aClient *cptr;

    for (i = 0; i <= highest_fd; i++)
	sentalong[i] = 0;
    for (cptr = client; cptr; cptr = cptr->next) {
	i = cptr->from->fd;	  /* find connection oper is on */
	if (sentalong[i])	  /* sent message along it already ? */
	    continue;
	if (cptr->from == one)
	    continue;		  /* ...was the one I should skip */
	sentalong[i] = 1;
	sendto_prefix_one (cptr->from, from, pattern,
			   p1, p2, p3, p4, p5, p6, p7, p8);
    }
    return;
}

/*
   ** sendto_ops_butone
   **   Send message to all operators regardless of whether they are +w or
   **   not..
   ** one - client not to send message to
   ** from- client which message is from *NEVER* NULL!!
 */
void sendto_opers_butone (one, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     aClient *one, *from;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    int i;
    aClient *cptr;

    for (i = 0; i <= highest_fd; i++)
	sentalong[i] = 0;
    for (cptr = client; cptr; cptr = cptr->next) {
	if (!IsAnOper (cptr))
	    continue;
	i = cptr->from->fd;	  /* find connection oper is on */
	if (sentalong[i])	  /* sent message along it already ? */
	    continue;
	if (cptr->from == one)
	    continue;		  /* ...was the one I should skip */
	sentalong[i] = 1;
	sendto_prefix_one (cptr->from, from, pattern,
			   p1, p2, p3, p4, p5, p6, p7, p8);
    }
    return;
}

/*
   ** sendto_ops_butme
   **   Send message to all operators except local ones
   ** from- client which message is from *NEVER* NULL!!
 */
void sendto_ops_butme (from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     aClient **from;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    int i;
    aClient *cptr;

    for (i = 0; i <= highest_fd; i++)
	sentalong[i] = 0;
    for (cptr = client; cptr; cptr = cptr->next) {
	i = cptr->from->fd;	  /* find connection oper is on */
	if (sentalong[i])	  /* sent message along it already ? */
	    continue;
	if (!strcmp (cptr->user->server, me.name))	/* a locop */
	    continue;
	sentalong[i] = 1;
	sendto_prefix_one (cptr->from, from, pattern,
			   p1, p2, p3, p4, p5, p6, p7, p8);
    }
    return;
}

/*
 * sendto_prefix_one()
 *
 * to - destination client
 * from - client which message is from
 *
 * NOTE: NEITHER OF THESE SHOULD *EVER* BE NULL!!
 * -avalon
 */
void sendto_prefix_one (to, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     aClient *to;
     aClient *from;
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    static char sender[HOSTLEN + NICKLEN + USERLEN + 5];
    anUser *user;
    char *par;
    int flag = 0;

    par = p1;

    if (to && from && MyClient (to) && IsPerson (from) &&
	!mycmp (par, from->name)) {
	user = from->user;
	(void) strcpy (sender, from->name);

	if (user) {
	    if (*user->username) {
		(void) strcat (sender, "!");
		(void) strcat (sender, user->username);
	    }
	    if (*user->host && !MyConnect (from)) {
		(void) strcat (sender, "@");
		if ((IsAnOper (to) && !IsAnOper (from)) || (to == from))
		    (void) strcat (sender, user->host);
		else
		    (void) strcat (sender, MaskHost (from));

		flag = 1;
	    }
	}
	/*
	   ** flag is used instead of index(sender, '@') for speed and
	   ** also since username/nick may have had a '@' in them. -avalon
	 */
	if (!flag && MyConnect (from) && *user->host) {
	    (void) strcat (sender, "@");
	    if ((IsAnOper (to) && !IsAnOper (from)) || (to == from))
		(void) strcat (sender, user->host);
	    else
		(void) strcat (sender, MaskHost (from));
	}
	par = sender;
    }
    sendto_one (to, pattern, par, p2, p3, p4, p5, p6, p7, p8);
}

/*
 * sendto_realops
 *
 *  Send to *local* ops only but NOT +s nonopers.
 */
void sendto_realops (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
{
    aClient *cptr;
    int i;
    char nbuf[1024];

    for (i = 0; i <= highest_fd; i++)
	if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
	    IsOper (cptr)) {
	    (void) sprintf (nbuf, ":%s NOTICE %s :*** Notice -- ",
			    me.name, cptr->name);
	    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
	    sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
	}
    return;
}
